package model.dao;

import it.mrcrack.exception.DataFormatException;
import it.mrcrack.exception.DataNotFoundException;
import it.mrcrack.exception.DataNotSetException;
import it.mrcrack.model.DBDAO;

import java.sql.SQLException;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.TreeSet;
import java.util.Vector;

import model.AcquisitionsFactory;
import model.bean.MancatoFunzionamento;
import model.bean.Misura;
import model.bean.Sensore;
import model.bean.compare.SensoreComparator;
import model.util.Connessione;

public class MisureDBDAO extends DBDAO implements AcquisitionsFactory<Misura>{
	/**
	 * Costruttore
	 * @param conn Connessione al db
	 * @throws DataFormatException la connessione passata non puo' essere null
	 * @throws SQLException 
	 * @throws DataNotFoundException Lanciata se nel database non esiste il sensore passato
	 */
	public MisureDBDAO(Connessione conn) throws DataFormatException, SQLException, DataNotFoundException{
		super(conn);
	}

	/**
	 * Crea un vector contenente tutte le misure presenti nel database
	 * @return vector di misure
	 * @throws SQLException
	 */
	@Override
	public List<Misura> get() throws SQLException {
		GregorianCalendar minimo,massimo;
		massimo=minimo=new GregorianCalendar(1970, 0, 1);
		try{
			st=conn.getConn().createStatement();
			rs=st.executeQuery("SELECT MIN(Ora) as minimo FROM Misure");
			minimo=new GregorianCalendar();
			minimo.setTime(new Date(rs.getLong("minimo")));
			rs.close();
			rs=st.executeQuery("SELECT MAX(Ora) as massimo FROM Misure");
			massimo=new GregorianCalendar();
			massimo.setTime(new Date(rs.getLong("massimo")));
		} finally {
			rs.close();
			st.close();
			rs=null;
			st=null;
		}
		return get(minimo,massimo);
	}

	/**
	 * Crea un vector contenente tutte le misure nel giorno passato
	 * @param data data ricerca
	 * @return vector di misure
	 * @throws SQLException
	 */
	@Override
	public List<Misura> get(GregorianCalendar data) throws SQLException{
		if(data==null)
			throw new NullPointerException("La data passata non puo' essere null");
		GregorianCalendar gci, gcf;
		gci=(GregorianCalendar)data.clone();
		gci.set(GregorianCalendar.HOUR_OF_DAY, 0);
		gci.set(GregorianCalendar.MINUTE, 0);
		gci.set(GregorianCalendar.SECOND, 0);
		gci.set(GregorianCalendar.MILLISECOND, 0);
		gcf=(GregorianCalendar)data.clone();
		gcf.set(GregorianCalendar.HOUR_OF_DAY, 23);
		gcf.set(GregorianCalendar.MINUTE, 59);
		gcf.set(GregorianCalendar.SECOND, 59);
		gcf.set(GregorianCalendar.MILLISECOND, 999);
		return get(gci, gcf);
	}

	/**
	 * Crea un vector contenente tutte le misure nel range passato
	 * @param dataIniz data inizio ricerca
	 * @param dataFine data fine ricerca
	 * @return vector di misure
	 * @throws SQLException
	 */
	@Override
	public List<Misura> get(GregorianCalendar dataIniz, GregorianCalendar dataFine) throws SQLException{
		if(dataIniz==null)
			throw new NullPointerException("La data di inizio passata non puo' essere null");
		if(dataFine==null)
			throw new NullPointerException("La data di fine passata non puo' essere null");
		Vector<Misura> vm=new Vector<Misura>();
		TreeSet<Sensore> ts=new TreeSet<Sensore>(new SensoreComparator());
		try {
			ps=conn.getConn().prepareStatement("SELECT * FROM Misure WHERE Ora BETWEEN ? AND ? ORDER BY Ora;");
			ps.setLong(1, dataIniz.getTimeInMillis());
			ps.setLong(2, dataFine.getTimeInMillis());
			rs=ps.executeQuery();
			while(rs.next()){
				Misura m=new Misura();
				GregorianCalendar gc=new GregorianCalendar();
				gc.setTime(new Date(rs.getLong("Ora")));
				try {
					m.setOra(gc);
					m.setDurata(rs.getLong("Durata"));
					m.setErrore(rs.getDouble("Errore"));
					m.setMisura(rs.getDouble("Misura"));
					SensoriDBDAO sdd=new SensoriDBDAO(conn);
					Sensore temps=sdd.get(rs.getInt("SensoriID"));
					ts.add(temps);
					m.setSensore(ts.subSet(temps, true, temps, true).first());
					vm.add(m);
				} catch (DataFormatException e) {
					e.printStackTrace();
				} catch (DataNotFoundException e) {
					e.printStackTrace();
				} catch (DataNotSetException e) {
					e.printStackTrace();
				}
			}
		} finally {
			rs.close();
			ps.close();
			rs=null;
			ps=null;
		}
		return vm;
	}

	/**
	 * Controlla se l'orario passato e' un orario valido, ovvero, se non va in conflitto con nessuna misura associata al sensore gia' presente nel db
	 * @param m misura da aggiungere
	 * @return true se la misura non va in conflitto con misure gia' presenti nel db, false altrimenti
	 * @throws DataFormatException Misura o sensore passati non totalmente settati
	 * @throws SQLException
	 * @throws DataNotFoundException Non esistono il sensore o il report nel db
	 */
	private boolean validaOra(Misura m) throws SQLException, DataFormatException, DataNotFoundException{
		boolean flag=true;
		if(m==null)
			throw new NullPointerException("La misura passata non puo' essere null");
		if(!m.impostato())
			throw new DataFormatException("La misura passata non e' totalmente settata");
		try {
			ps=conn.getConn().prepareStatement("SELECT * FROM Misure " +
					"WHERE ((Ora < ? AND ? < (Ora+Durata*1000)) OR " +
					"(Ora < ? AND ? < (Ora+Durata*1000)) OR " +
			"(? < Ora AND (Ora+Durata*1000) < ?)) AND SensoriID=?;");
			ps.setLong(1, m.getOra().getTimeInMillis());
			ps.setLong(2, m.getOra().getTimeInMillis());
			ps.setLong(3, m.getOra().getTimeInMillis()+m.getDurata()*1000);
			ps.setLong(4, m.getOra().getTimeInMillis()+m.getDurata()*1000);
			ps.setLong(5, m.getOra().getTimeInMillis());
			ps.setLong(6, m.getOra().getTimeInMillis()+m.getDurata()*1000);
			SensoriDBDAO sdd=new SensoriDBDAO(conn);
			ps.setInt(7, sdd.getId(m.getSensore()));
			rs=ps.executeQuery();
			if(rs.next()){
				flag=false;
			}
		} finally {
			rs.close();
			ps.close();
			rs=null;
			ps=null;
		}
		return flag;
	}

	/**
	 * Controlla se l'orario passato e' un orario valido, ovvero, se non va in conflitto con nessuna misura associata al sensore gia' presente nel db
	 * @param m misura da aggiungere
	 * @param s sensore a cui associare la misura
	 * @return true se la misura non va in conflitto con misure gia' presenti nel db, false altrimenti
	 * @throws DataFormatException MancatoFunzionamento passato non totalmente settato
	 * @throws SQLException
	 * @throws DataNotFoundException Non esiste il report nel db
	 */
	boolean validaOra(MancatoFunzionamento m) throws SQLException, DataFormatException{
		boolean flag=true;
		if(m==null)
			throw new NullPointerException("Il mancato funzionamento passato non puo' essere null");
		if(!m.impostato())
			throw new DataFormatException("Il mancato funzionamento passato non e' totalmente settata");
		try {
			ps=conn.getConn().prepareStatement("SELECT * FROM Misure " +
					"WHERE ((Ora < ? AND ? < (Ora+Durata*1000)) OR " +
					"(Ora < ? AND ? < (Ora+Durata*1000)) OR " +
			"(? < Ora AND (Ora+Durata*1000) < ?));");
			ps.setLong(1, m.getOra().getTimeInMillis());
			ps.setLong(2, m.getOra().getTimeInMillis());
			ps.setLong(3, m.getOra().getTimeInMillis()+m.getDurata()*1000);
			ps.setLong(4, m.getOra().getTimeInMillis()+m.getDurata()*1000);
			ps.setLong(5, m.getOra().getTimeInMillis());
			ps.setLong(6, m.getOra().getTimeInMillis()+m.getDurata()*1000);
			rs=ps.executeQuery();
			if(rs.next()){
				flag=false;
			}
		} finally {
			rs.close();
			ps.close();
			rs=null;
			ps=null;
		}
		return flag;
	}

	/**
	 * Aggiunge una misura al database
	 * @param m misura da aggiungere
	 * @return true se riesce ad aggiungere la misura, false altrimenti
	 * @throws DataFormatException Misura o sensore passati non totalmente settati
	 * @throws SQLException
	 * @throws DataNotFoundException Non esistono il sensore o il report nel db
	 */
	public boolean put(Misura m) throws DataFormatException, SQLException, DataNotFoundException{
		boolean flag=false;
		if(m==null)
			throw new NullPointerException("La misura passata non puo' essere null");
		if(!m.impostato())
			throw new DataFormatException("La misura passata non e' totalmente settata");
		if(validaOra(m)){
			try{
				ps=conn.getConn().prepareStatement("INSERT INTO Misure (Ora, Durata, Misura, Errore, " +
				"SensoriID, ReportsID) VALUES (?,?,?,?,?,?);");
				ps.setLong(1, m.getOra().getTimeInMillis());
				ps.setLong(2, m.getDurata());
				ps.setDouble(3, m.getMisura());
				ps.setDouble(4, m.getErrore());
				ReportsDBDAO rd=new ReportsDBDAO(conn);
				SensoriDBDAO sdd=new SensoriDBDAO(conn);
				ps.setInt(5, sdd.getId(m.getSensore()));
				ps.setInt(6, rd.getId(m.getOra()));
				if(ps.executeUpdate()>0)
					flag=true;
			} finally {
				ps.close();
				ps=null;
			}
		}
		return flag;
	}
}